﻿using System;
using System.Security.Cryptography;
using System.IO;

namespace OpusSuite.Utility
{
    public static class CryptoUtil
    {
        private const int MinSaltSize = 10;
        private const int MaxSaltSize = 10;

        public static string EncryptStringAES(string plainText, string sharedSecret, string base64Salt="")     
        {         
            if(string.IsNullOrEmpty(plainText))             
                throw new ArgumentNullException("plainText");         
            if(string.IsNullOrEmpty(sharedSecret))             
                throw new ArgumentNullException("sharedSecret");
            string outStr;                       // Encrypted string to return         
            RijndaelManaged aesAlg = null;              // RijndaelManaged object used to encrypt the data.
            try
            {
                byte[] salt;
                // Define min and max salt sizes.
                if (base64Salt.Length == 0)
                {
                    // Generate a random number for the size of the salt.
                    Random random = new Random();
                    int saltSize = random.Next(MinSaltSize, MaxSaltSize);

                    // Allocate a byte array, which will hold the salt.
                    salt = new byte[saltSize];

                    // Initialize a random number generator.
                    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();

                    // Fill the salt with cryptographically strong byte values.
                    rng.GetNonZeroBytes(salt);
                }
                else
               {
                   salt = Convert.FromBase64String(base64Salt);
                }

                // generate the key from the shared secret and the salt             
                var key = new Rfc2898DeriveBytes(sharedSecret, salt);
                // Create a RijndaelManaged object             
                // with the specified key and IV.             
                aesAlg = new RijndaelManaged();
                aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
                aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
                // Create a decrytor to perform the stream transform.             
                ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
                // Create the streams used for encryption.             
                using (var msEncrypt = new MemoryStream())
                {
                    using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (var swEncrypt = new StreamWriter(csEncrypt))
                        {
                            //Write all data to the stream.                         
                            swEncrypt.Write(plainText);
                        }
                    }
                    byte[] s = new byte[10 + msEncrypt.ToArray().GetLength(0)];
                    salt.CopyTo(s, 0);
                    msEncrypt.ToArray().CopyTo(s, 10);
                    outStr = Convert.ToBase64String(s);
                    //base64Salt = Convert.ToBase64String(salt);
                }
            }
            catch
            {
                outStr = "";
            }
            finally         
            {             
                // Clear the RijndaelManaged object.             
                if (aesAlg != null)                 
                    aesAlg.Clear();         
            }          

            return outStr;     
        }

        public static string DecryptStringAES(string cipherText, string sharedSecret)
        {
            if (string.IsNullOrEmpty(cipherText))
                throw new ArgumentNullException("cipherText");
            if (string.IsNullOrEmpty(sharedSecret))
                throw new ArgumentNullException("sharedSecret");
            // Declare the RijndaelManaged object         
            // used to decrypt the data.         
            RijndaelManaged aesAlg = null;
            // Declare the string used to hold         
            // the decrypted text.         
            string plaintext;
            try
            {
                byte[] cipher = Convert.FromBase64String(cipherText);
                byte[] salt = new byte[MaxSaltSize];
                byte[] bytes = new byte[cipher.Length - 10];

                for (int i = 0; i < 10; i++)
                    salt[i] = cipher[i];

                for (int i = 10; i < cipher.Length; i++)
                    bytes[i - 10] = cipher[i];

                // generate the key from the shared secret and the salt             
                var key = new Rfc2898DeriveBytes(sharedSecret, salt);
                // Create a RijndaelManaged object             
                // with the specified key and IV.             
                aesAlg = new RijndaelManaged();
                aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
                aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
                // Create a decrytor to perform the stream transform.             
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
                // Create the streams used for decryption.                             
                using (var msDecrypt = new MemoryStream(bytes))
                {
                    using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (var srDecrypt = new StreamReader(csDecrypt))
                            // Read the decrypted bytes from the decrypting stream                         
                            // and place them in a string.                         
                            plaintext = srDecrypt.ReadToEnd();
                    }
                }
            }
            catch
            {
                plaintext = "";
            }
            finally
            {
                // Clear the RijndaelManaged object.             
                if (aesAlg != null)
                    aesAlg.Clear();
            }
            return plaintext;
        }

        public static string GetSalt(string cipherText)
        {
            if (string.IsNullOrEmpty(cipherText))
                throw new ArgumentNullException("cipherText");

            string saltBase64 = "";

            try
            {
                byte[] cipher = Convert.FromBase64String(cipherText);
                byte[] salt = new byte[MaxSaltSize];

                for (int i = 0; i < 10; i++)
                    salt[i] = cipher[i];

                saltBase64 = Convert.ToBase64String(salt);
            }
            catch
            {
            }

            return saltBase64;
        }
    } 
}
